// gcc pipe.c

/* 客户-服务器模型
客户端从标准输入获取请求文件的路径，用管道传给服务器；
服务器从管道获得文件路径后，读取文件内容，把文件内容写入到另一管道；
客户从管道获取文件内容后 把其显示在标准输出。

此启动父子进程不需要同步：
若server先启动，它会阻塞在read上（等待client向管道写入消息）；
若client先启动，它会阻塞在fgets获取用户请求上。
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>


#define MAXLINE 1024
#define _print(note, ...) \
    printf("\n===============\n" note "\n===============\n",  ##__VA_ARGS__)



void client(int pipefd_content_0,   // 读管道：从服务端传出的文档内容
            int pipefd_path_1);     // 写管道：用于传输文件路径

void server(int pipefd_path_0,      // 读管道：传入客户端写入的文件路径
            int pipefd_content_1);  // 写管道：读取文件内容后写入到管道


int main(int argc, char** argv)
{
    int pipe_path[2], pipe_file[2];
    pipe(pipe_path);  // 管道1：用于传输打开文件路径
    pipe(pipe_file);  // 管道2：用于传输文件读取的内容

    pid_t child_pid;
    if((child_pid = fork()) == 0){
        //关闭管道2的写描述符和管道1的读描述符
        close(pipe_path[1]);
        close(pipe_file[0]);

        //启动服务器函数
        server(pipe_path[0], pipe_file[1]);
        exit(0);
    }else{
        //关闭管道2的读描述符和管道1的写描述符
        close(pipe_file[1]);
        close(pipe_path[0]);

        //启动客户函数
        client(pipe_file[0], pipe_path[1]);

        //等待子进程结束
        waitpid(child_pid, NULL, 0);
    }

    exit(0);
}



void client(int pipefd_content_0,  // 读管道：从服务端传出的文档内容
			int pipefd_path_1)     // 写管道：用于传输文件路径
{
puts("-->> Step into function_client()");
    char buffer[MAXLINE] = {};

    //从标准输入获取用户的请求（文件路径）
puts("Please Input a file path to read : ");
    fgets(buffer, MAXLINE, stdin);
    int buf_len = strlen(buffer);
    if(buffer[buf_len - 1] == '\n')  buf_len--;
_print("The file path : %s, len=%d", buffer, buf_len);

    //把用户请求的文件路径写入管道
    write(pipefd_path_1, buffer, buf_len);

    //从管道中读取服务器端的响应消息，并输出到标准输出
puts("-->> [client] Waiting for server-file output.");
    int n;
    while((n = read(pipefd_content_0, buffer, MAXLINE)) > 0){
_print("Read from Pipe_Read_fd --> [strlen = %d]", n);
        write(STDOUT_FILENO, buffer, n);
    }
}

void server(int pipefd_path_0,     // 读管道：传入客户端写入的文件路径
			int pipefd_content_1)  // 写管道：读取文件内容后写入到管道
{
puts("-->> Step into function_server()");
    int fd;
    ssize_t n;
    char buffer[MAXLINE+1] = {};

    //从管道读取来自客户端的请求消息
puts("-->> [server] Waiting for client file_path");
    if((n = read(pipefd_path_0, buffer, MAXLINE)) == 0)
        perror("EOF while reading pathname");

    //以只读方式打开用户请求的文件
    if((fd = open(buffer, O_RDONLY)) < 0){
puts("-->> [server] Fail to open file.");
        snprintf(buffer + n, sizeof(buffer)-n,":can't open, %s\n", strerror(errno));
        n = strlen(buffer);
        write(pipefd_content_1, buffer, n);
    }else{
puts("-->> [server] Reading from the file.");
        while((n = read(fd, buffer, MAXLINE))> 0)
            write(pipefd_content_1, buffer, n);
        close(fd);
    }
}

